/***********************************************
 **** 版权声明处 **
 ****  为了方便阅读和维护，请严格遵守相关代码规范，谢谢   ****
 *******************************************/
package com.hnisi
{
	import com.hnisi.events.GraphEvent;
	
	import flash.display.DisplayObject;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	
	import mx.core.FlexGlobals;
	
	import spark.components.supportClasses.SkinnableComponent;
	import spark.events.RendererExistenceEvent;
	
	/**
	 * The state indicating that the renderer is highlighted but not selected.  
	 */
	[SkinState("highlighted")]
	
	/**
	 * The state indicating that the renderer is selected but not highlighted.  
	 */
	[SkinState("selected")]
	
	/**
	 * The state indicating that the renderer is highlighted and selected.  
	 */
	[SkinState("selectedHighlighted")]
	
	/*******************************************
	 **** @author huangjixin,@date 2012-6-26, @time 下午5:10:35 **
	 **** The Adorner class is the base class of all adorner objects created by a DiagramEditor to give visual feedback and handle object-specific interactions when editing a Diagram. 
	 * 
	 * An adorner is a graphical object that is displayed on top of the highlighted or selected renderers (nodes or links) contained in a Diagram. The adorner displays some kind of visual feedback to highlight the renderer. The basic behavior is to add a "glow" filter to the base shape of the renderer.
	 * 
	 * Adorners typically display "handles", which are small graphical objects that react to mouse events and implement a specific editing interaction. For example, an adorner can display handles near the corners of a node to resize the node. The Adorner base class implements all the logic that dispatches events to handles. The handles are usually subclasses of AdornerHandle, a simple base class that implements a highlighting mechanism.
	 * 
	 * The Adorner class is also used to describe some editing-related characteristics of a renderer. For example, the canMove property tells the editor whether the renderer can be moved, and a set of methods (including getEditableTextParts(), getText(), and setText()) describes whether and how text can be edited for the renderer.
	 * 
	 * Diagram for Flex provides some predefined adorners and handles for common cases: resizing nodes, connecting links, and reparenting to subgraphs.
	 * 
	 * The type of adorner that will be created for a given renderer class is defined through styling, using the adornerClass style. By default, the adorner class for Node objects is NodeAdorner, the default adorner class for Link objects is LinkAdorner, and the default adorner class for Subgraph objects is SubgraphAdorner.
	 * 
	 * You can create a custom adorner to add specific interactions and/or to give custom visual feedback for an existing or a custom renderer. To do this, you create a subclass of Adorner or of one of its subclasses, and you associate the renderer class with the new renderer class by adding a style rule such as:
	 * 
	 * Adorners and adorner handles are all skinnable components. As with all skinnable components, you can change the look of adorners and adorner handles by defining custom skins. When an adorner uses handles, the handles are defined as skin parts. Adorner skins must define three states:
	 * selected: used when the renderer is selected but the mouse is not over it
	 * highlighted: used when the mouse is over the renderer but the renderer is not selected
	 * selectedHighlighted: used when the mouse is over the renderer and the renderer is selected 
	 *******************************************/
	public class Adorner extends SkinnableComponent
	{
		//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
		// private 类私有静态变量和静态常量声明处。（全部大写，使用下划线进行分割）
		// 例如：private static const EXAMPLE:String = "example";
		//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
		
		//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
		// public 类公有静态变量和静态常量声明处。（全部大写，使用下划线进行分割）
		// 例如：public static const EXAMPLE:String = "example";
		//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
		
		
		//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
		// private 私有变量声明处，请以“_”开头定义变量
		// 例如：private var _example:String;
		//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
		
		
		//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
		// public 公有变量声明处
		//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
		private var _adornedObject : Node;

		private var _canCopyPaste : Boolean;

		private var _canMove : Boolean;

		private var _canReparent : Boolean;
		
		
		/**
		 *  The DiagramEditor in which this adorner is contained.
		 */
		public var editor : Diagram;
		/**
		 * The Graph in which the adorned renderer is contained. 
		 */
		public var graph : Graph;
		/**
		 * A boolean indicating whether or not the adorner is highlighted (usually when the mouse is over the renderer). 
		 */
		public var highlighted : Boolean;
		/**
		 * A boolean indicating whether or not the adorned object is selected. 
		 */
		public var selected : Boolean;
		
		protected var tolerance:Number = 4;
		//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
		// 构造函数，初始化相关工作可以放在里面
		//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
		/**
		 * 构造函数.
		 * @param adornedObject
		 * 
		 */
		public function Adorner(adornedObject:Node)
		{
			this._adornedObject = adornedObject;
			this.adornedObject.addEventListener(MouseEvent.MOUSE_MOVE,onAdornerObjectMove);
			this.adornedObject.stage.addEventListener(MouseEvent.MOUSE_MOVE,onAdornerObjectMove);
			this.adornedObject.addEventListener(MouseEvent.ROLL_OUT,onAdonerRollOut);
			this.adornedObject.addEventListener(GraphEvent.NODE_REMOVED,onNodeRemoved);
			this.adornedObject.addEventListener("positionChange",onPositionChange);
			super();
			
		}//构造函数结束
		
		
		//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
		// getter和setter函数
		//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
		/**
		 *	The renderer with which this adorner is associated 
		 * @return 
		 * 
		 */
		public function get adornedObject():Node
		{
			return _adornedObject;
		}
		
		/**
		 * A boolean indicating whether the renderer can be copied and pasted (that is, using the copy()/paste() functions of the DiagramEditor) or not. 
		 * @return 
		 * 
		 */
		public function get canCopyPaste():Boolean
		{
			return _canCopyPaste;
		}
		
		/**
		 * A boolean indicating whether the renderer can be moved or not. 
		 * @return 
		 * 
		 */
		public function get canMove():Boolean
		{
			return _canMove;
		}

		/**
		 * A boolean indicating whether the renderer can be reparented (that is, dragged to a different Subgraph) or not. 
		 * @return 
		 * 
		 */
		public function get canReparent():Boolean
		{
			return _canReparent;
		}

		
		//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
		// 相关事件响应函数和逻辑函数存放处
		//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
		protected function onPositionChange(event:Event):void
		{
			if(!AdonerPlayer.playIng){
				play();			
			}
		}
		
		protected function onNodeRemoved(event:Event):void
		{
			cleanup();
			this.adornedObject.removeEventListener(GraphEvent.NODE_REMOVED,onNodeRemoved);
			this.adornedObject.removeEventListener(MouseEvent.MOUSE_MOVE,onAdornerObjectMove);
			this.adornedObject.stage.removeEventListener(MouseEvent.MOUSE_MOVE,onAdornerObjectMove);
			this.adornedObject.removeEventListener(MouseEvent.ROLL_OUT,onAdonerRollOut);
		}
		
		protected function onAdonerRollOut(event:MouseEvent):void
		{
			cleanup();
			this.adornedObject.removeEventListener(MouseEvent.ROLL_OUT,onAdonerRollOut);
		}
		
		protected function onMouseMove(event:MouseEvent):void
		{
			
		}

		public function play():void
		{
			this.width = this.adornedObject.width;
			this.height = this.adornedObject.height;
			var point:Point = new Point();
			point = this.adornedObject.localToGlobal(point);
			point = FlexGlobals.topLevelApplication.globalToLocal(point);
			if(!FlexGlobals.topLevelApplication.contains(this)){
				FlexGlobals.topLevelApplication.addElement(this);
			}
			
			this.x = point.x;
			this.y = point.y;
			
			AdonerPlayer.playIng = true;
		}//play结束
		/**
		 * 侦听移动，更新边饰； 
		 * @param event
		 * 
		 */
		protected function onAdornerObjectMove(event:MouseEvent):void
		{
			if(!isMouseNear(tolerance)){
				cleanup();
				return;
			}
			
			this.width = this.adornedObject.width;
			this.height = this.adornedObject.height;
			var point:Point = new Point();
			point = this.adornedObject.localToGlobal(point);
			point = FlexGlobals.topLevelApplication.globalToLocal(point);
			if(!FlexGlobals.topLevelApplication.contains(this)){
				FlexGlobals.topLevelApplication.addElement(this);
			}
			
			this.x = point.x;
			this.y = point.y;
			
			AdonerPlayer.playIng = true;
		}
		/**
		 * 
		 * @param ancestor An ancestor of the adorned renderer. 
		 * @return true if the ancestor should be highlighted as well as the adorned renderer, false otherwise. 
		 * 
		 */
		public function canHighlightAncestor(ancestor:Render):Boolean{
			return false;
		}
		
		/**
		 * This method is called when an adorner is removed (that is, when the renderer is not selected or highlighted any more). 
		 * This method can be overridden to perform some specific cleanup work. In this case, the subclass must call the cleanup method of the base class. 
		 */
		protected function cleanup():void{
			if(FlexGlobals.topLevelApplication.contains(this)){
				FlexGlobals.topLevelApplication.removeElement(this);
				
				AdonerPlayer.playIng = false;
			}
		}
		
		/**
		 * Returns the rectangle that defines the position and the size of the adorner. The default implementation returns the bounds of the renderer's base. This method can be overridden to display an adorner around a specific area of a renderer. 
		 * @param targetCoordinateSpace Determines the coordinate space of the returned rectangle.  
		 * @return A rectangle in the coordinate space of the targetCoordinateSpace object.
		 * 
		 */
		protected function getAdornerRectangle(targetCoordinateSpace:DisplayObject):Rectangle{
			return new Rectangle();
		}
		
		/**
		 * Gets the text of a renderer. 
		 * @param textPart One of the objects contained in the vector returned by getTextEditableTextParts(). 
		 * @return The text displays by the specified text part.
		 * 
		 */
		public function getText(textPart:Object):String{
			return "";
		}
		
		/**
		 * Sets the text of a renderer. 
		 * @param textPart One of the objects contained in the vector returned by getTextEditableTextParts(). 
		 * @param newText The new text to be displayed by the specified text part.
		 * 
		 */
		public function setText(textPart:Object, newText:String):void{
			
		}
		/**
		 * This method is called when the user moves the mouse with a mouse button pressed in a handle of the adorner.
		 * Note: This method is called only if isHandle(handle) returned true.
		 * @param handle
		 * @param event
		 * @param dx
		 * @param dy
		 * 
		 */
		protected function handleDragged(handle:DisplayObject, event:MouseEvent, dx:Number, dy:Number):void{
		
		}
		
		/**
		 * This method is called when the user presses a mouse button in a handle of the adorner. 
		 * Note: This method is called only if isHandle(handle) returned true. 
		 * @param handle The adorner handle.
		 * @param event The mouse event.
		 * 
		 */
		protected function handlePressed(handle:DisplayObject, event:MouseEvent):void{
		
		}
		
		/**
		 * This method is called when the user releases a mouse button in a handle of the adorner. 
		 * @param handle The adorner handle.
		 * @param event The mouse event.
		 * 
		 */
		protected function handleReleased(handle:DisplayObject, event:MouseEvent):void{
		
		}
		
		/**
		 * This method is called to determine whether the specified skin part is a handle of the adorner. If this method returns true, the adorner will watch mouse events on the object and call the handlePressed, handleDragged, and handleReleased methods for this object. 
		 * @param part A skin part of this adorner.
		 * @return true if the specified skin part is a handle and if the adorner should process mouse events for it, or false otherwise. 
		 * 
		 */
		protected function isHandle(part:Object):Boolean{
			return part is Handle;
		}
		
		/**
		 * Determines whether the mouse is currently near this adorner.
		 * 
		 * This method is used to avoid an object becoming unhighlighted when the user moves the mouse slightly out of the strict bounds of a renderer. For example, to resize a node, the user will often move the mouse slightly beyond the corner of the node. Therefore, this method performs a hit test with a small tolerance to keep the node highlighted even if the mouse is slightly outside the node's bounds.
		 * 
		 * The default implementation does a hit test on the bounds of the adorner augmented by the tolerance value. Subclasses can perform a different hit test depending on the shape of the renderer. 
		 * @param tolerance The tolerance around the object (in pixels). 
		 * @return true if the mouse is near the adorner, false otherwise.
		 * 
		 */
		public function isMouseNear(tolerance:Number):Boolean{
			return false;
		}
		/*this.adornedObject.stage.mouseY > -tolerance && this.adornedObject.stage.mouseY < this.adornedObject.stage.y + this.adornedObject.height + tolerance*/
		/**
		 * Indicates if the text part contains multiline text.
		 *  
		 * @param textPart One of the objects contained in the vector returned by getTextEditableTextParts(). 
		 * @return true if the text has multiple lines. 
		 * 
		 */
		public function isMultiline(textPart:Object):Boolean{
			return false;
		}
		//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
		// override 覆盖函数
		//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
		override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void{
			super.updateDisplayList(unscaledWidth,unscaledHeight);
		} 
	}//类结束
}//包结束